﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows.Media;

namespace FirstFloor.Documents.Pdf.Fonts
{
    /// <summary>
    /// Represents the base class for all fonts.
    /// </summary>
    internal abstract class FontBase
    {
        private string fontType;

        /// <summary>
        /// Initializes a new instance of the <see cref="FontBase"/> class.
        /// </summary>
        /// <param name="fontDictionary">The font dictionary.</param>
        public FontBase(DictionaryObject fontDictionary)
        {
            if (fontDictionary == null) {
                throw new ArgumentNullException("fontDictionary");
            }
            if (fontDictionary.Type != "Font") {
                throw new ArgumentException(Resources.InvalidFontDictionary);
            }
            this.FontDictionary = fontDictionary;
            this.FontDescriptor = fontDictionary.Get<DictionaryObject>("FontDescriptor", null);
            if (this.FontDescriptor != null) {
                this.FontFlags = (FontFlags)this.FontDescriptor.Get<int>("Flags");
            }
            this.StyleSimulation = StyleSimulations.None;

            this.Encoding = new FontEncoding(fontDictionary, this.FontFlags);

            var toUnicode = this.FontDictionary.Get<DictionaryObject>("ToUnicode", null);
            if (toUnicode != null) {
            }


            // by default font is assumed to be embedded, overridable in subclasses
            this.RequestUri = RequestHelper.CreateFontRequestUri(fontDictionary.Identifier.Value);
        }

        /// <summary>
        /// Gets the font dictionary.
        /// </summary>
        /// <value>The font dictionary.</value>
        public DictionaryObject FontDictionary { get; private set; }
        /// <summary>
        /// Gets the font descriptor.
        /// </summary>
        /// <value>The font descriptor.</value>
        public DictionaryObject FontDescriptor { get; protected set; }
        /// <summary>
        /// Gets the font encoding.
        /// </summary>
        /// <value>The encoding.</value>
        public FontEncoding Encoding { get; private set; }
        /// <summary>
        /// Gets the name of the font.
        /// </summary>
        /// <value>The name of the font.</value>
        public string FontName { get; protected set; }
        /// <summary>
        /// Defines various characteristics of the font
        /// </summary>
        /// <value>The font flags.</value>
        public FontFlags FontFlags { get; private set; }
        /// <summary>
        /// Determines whether this font contains a subset.
        /// </summary>
        /// <value><c>true</c> if this instance is subset; otherwise, <c>false</c>.</value>
        public bool IsSubset { get; protected set; }
        /// <summary>
        /// Gets the request uri associated with this font.
        /// </summary>
        /// <value>The font URI.</value>
        public string RequestUri { get; protected set; }
        /// <summary>
        /// Gets the type of the font.
        /// </summary>
        /// <value>The type of the font.</value>
        public string FontType
        {
            get
            {
                if (fontType == null) {
                    fontType = this.FontDictionary.Get<string>("Subtype");
                }
                return fontType;
            }
        }

        /// <summary>
        /// Gets or sets the style simulation.
        /// </summary>
        /// <value>The style simulation.</value>
        public StyleSimulations StyleSimulation { get; protected set; }
        /// <summary>
        /// Gets the embedded font file.
        /// </summary>
        /// <returns></returns>
        public virtual Stream GetFontFile()
        {
            return null;
        }

        /// <summary>
        /// Parses the glyph content of specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The parsed content</returns>
        public GlyphsContent ParseGlyphContent(IEnumerable<object> value)
        {
            var indices = new List<GlyphIndexPoint>();

            foreach (var part in value) {
                var s = part as string;
                if (s != null) {
                    var i = ParseIndices(s);
                    if (i == null) {
                        return null;
                    }
                    indices.AddRange(i);

                    continue;
                }
                var bytes = part as byte[];
                if (bytes != null) {
                    var i = ParseIndices(bytes);
                    if (i == null) {
                        return null;
                    }
                    indices.AddRange(i);

                    continue;
                }
                var d = part as double?;
                if (d.HasValue && indices.Count > 0) {
                    // advance the width of the last glyph index point
                    var index = indices[indices.Count - 1];
                    indices[indices.Count - 1] = index.Advance(-d.Value);
                }
                else {
                }
            }

            return new GlyphsContent(indices.ToArray());
        }

        /// <summary>
        /// Parses the Glyph indices from specified string value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        protected virtual IEnumerable<GlyphIndexPoint> ParseIndices(string value)
        {
            return null;
        }

        /// <summary>
        /// Parses the Glyph indices from specified string value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        protected virtual IEnumerable<GlyphIndexPoint> ParseIndices(byte[] value)
        {
            return null;
        }

        /// <summary>
        /// Creates the font.
        /// </summary>
        /// <param name="fontDictionary">The font dictionary.</param>
        /// <returns></returns>
        public static FontBase CreateFont(DictionaryObject fontDictionary)
        {
            var fontType = fontDictionary.Get<string>("Subtype");

            if (fontType == "Type0") {
                var descendantFonts = fontDictionary.Get<ArrayObject>("DescendantFonts");
                var cidFontDictionary = descendantFonts.Get<DictionaryObject>(0);
                var cidFontType = cidFontDictionary.Get<string>("Subtype");
                var fontDescriptor = cidFontDictionary.Get<DictionaryObject>("FontDescriptor");

                if (cidFontType == "CIDFontType2" && fontDescriptor.ContainsKey("FontFile2")) {
                    return new Type0Font(fontDictionary);
                }
                else {
                    return StandardType1Font.CreateSubstituteFont(cidFontDictionary);
                }
            }
            else if (fontType == "Type1") {
                var fontName = fontDictionary.Get<string>("BaseFont");
                if (StandardFonts.IsStandardFontName(fontName)) {
                    return StandardType1Font.CreateStandardType1Font(fontName, fontDictionary);
                }
                else {
                    Debug.WriteLine("Substituting Type 1 font with Standard Type 1 alternative");
                    return StandardType1Font.CreateSubstituteFont(fontDictionary);
                }
            }
            else if (fontType == "MMType1") {
            }
            else if (fontType == "Type3") {
            }
            else if (fontType == "TrueType") {
                if (TrueTypeFont.IsFontFileEmbedded(fontDictionary)) {
                    return new TrueTypeFont(fontDictionary);
                }
                else {
                    Debug.WriteLine("Substituting TrueType font without font descriptor with Standard Type 1 alternative");

                    var fontName = fontDictionary.Get<string>("BaseFont");
                    return StandardType1Font.CreateStandardType1Font(fontName, fontDictionary);
                }
            }

           // throw new NotSupportedException(string.Format(Resources.UnsupportedFontType, fontType));
            return null;
        }
    }
}
